Provide a simpler, higher-level interface for mapping in pages from another domain.
authorkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Thu, 16 Feb 2006 22:37:40 +0000 (23:37 +0100)
committerkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Thu, 16 Feb 2006 22:37:40 +0000 (23:37 +0100)
I also added a one-liner that causes xenbus_dev_(error|fatal) to output
to the kernel log buffer (and thus syslog) which was invaluable to me
while debugging (by default, error messages only appear in xenstore).

Signed-off-by: Ryan Wilson <hap9@epoch.ncsc.mil>
linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c
linux-2.6-xen-sparse/include/xen/xenbus.h

index 530d303accf13386ba1f9ae56acf40a5b992c2de..444f0e9f0da36a26b33f2fea2d2c1190fad82226 100644 (file)
@@ -30,6 +30,7 @@
 #include <xen/evtchn.h>
 #include <xen/gnttab.h>
 #include <xen/xenbus.h>
+#include <xen/driver_util.h>
 
 /* xenbus_probe.c */
 extern char *kasprintf(const char *fmt, ...);
@@ -139,6 +140,8 @@ void _dev_error(struct xenbus_device *dev, int err, const char *fmt,
        BUG_ON(len + ret > PRINTF_BUFFER_SIZE-1);
        dev->has_error = 1;
 
+       dev_err(&dev->dev, "%s\n", printf_buffer);
+
        path_buffer = error_path(dev);
 
        if (path_buffer == NULL) {
@@ -202,7 +205,8 @@ int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port)
        evtchn_op_t op = {
                .cmd = EVTCHNOP_alloc_unbound,
                .u.alloc_unbound.dom = DOMID_SELF,
-               .u.alloc_unbound.remote_dom = dev->otherend_id };
+               .u.alloc_unbound.remote_dom = dev->otherend_id
+       };
 
        int err = HYPERVISOR_event_channel_op(&op);
        if (err)
@@ -214,6 +218,167 @@ int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port)
 EXPORT_SYMBOL(xenbus_alloc_evtchn);
 
 
+int xenbus_bind_evtchn(struct xenbus_device *dev, int remote_port, int *port)
+{
+       evtchn_op_t op = {
+               .cmd = EVTCHNOP_bind_interdomain,
+               .u.bind_interdomain.remote_dom = dev->otherend_id,
+               .u.bind_interdomain.remote_port = remote_port,
+       };
+
+       int err = HYPERVISOR_event_channel_op(&op);
+       if (err)
+               xenbus_dev_fatal(dev, err,
+                                "binding to event channel %d from domain %d",
+                                remote_port, dev->otherend_id);
+       else
+               *port = op.u.bind_interdomain.local_port;
+       return err;
+}
+EXPORT_SYMBOL(xenbus_bind_evtchn);
+
+
+int xenbus_free_evtchn(struct xenbus_device *dev, int port)
+{
+       evtchn_op_t op = {
+               .cmd = EVTCHNOP_close,
+               .u.close.port = port,
+       };
+       int err = HYPERVISOR_event_channel_op(&op);
+       if (err)
+               xenbus_dev_error(dev, err, "freeing event channel %d", port);
+       return err;
+}
+
+
+/* Based on Rusty Russell's skeleton driver's map_page */
+int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr)
+{
+       struct gnttab_map_grant_ref op = {
+               .flags = GNTMAP_host_map,
+               .ref   = gnt_ref,
+               .dom   = dev->otherend_id,
+       };
+       struct vm_struct *area;
+
+       *vaddr = NULL;
+
+       area = alloc_vm_area(PAGE_SIZE);
+       if (!area)
+               return -ENOMEM;
+
+       op.host_addr = (unsigned long)area->addr;
+
+       lock_vm_area(area);
+       BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1));
+       unlock_vm_area(area);
+
+       if (op.status != GNTST_okay) {
+               free_vm_area(area);
+               xenbus_dev_fatal(dev, op.status,
+                                "mapping in shared page %d from domain %d",
+                                gnt_ref, dev->otherend_id);
+               return op.status;
+       }
+
+       /* Stuff the handle in an unused field */
+       area->phys_addr = (unsigned long)op.handle;
+
+       *vaddr = area->addr;
+       return 0;
+}
+EXPORT_SYMBOL(xenbus_map_ring_valloc);
+
+
+int xenbus_map_ring(struct xenbus_device *dev, int gnt_ref,
+                  grant_handle_t *handle, void *vaddr)
+{
+       struct gnttab_map_grant_ref op = {
+               .host_addr = (unsigned long)vaddr,
+               .flags     = GNTMAP_host_map,
+               .ref       = gnt_ref,
+               .dom       = dev->otherend_id,
+       };
+
+       BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1));
+
+       if (op.status != GNTST_okay) {
+               xenbus_dev_fatal(dev, op.status,
+                                "mapping in shared page %d from domain %d",
+                                gnt_ref, dev->otherend_id);
+       } else
+               *handle = op.handle;
+
+       return op.status;
+}
+EXPORT_SYMBOL(xenbus_map_ring);
+
+
+/* Based on Rusty Russell's skeleton driver's unmap_page */
+int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr)
+{
+       struct vm_struct *area;
+       struct gnttab_unmap_grant_ref op = {
+               .host_addr = (unsigned long)vaddr,
+       };
+
+       /* It'd be nice if linux/vmalloc.h provided a find_vm_area(void *addr)
+        * method so that we don't have to muck with vmalloc internals here.
+        * We could force the user to hang on to their struct vm_struct from
+        * xenbus_map_ring_valloc, but these 6 lines considerably simplify
+        * this API.
+        */
+       read_lock(&vmlist_lock);
+       for (area = vmlist; area != NULL; area = area->next) {
+               if (area->addr == vaddr)
+                       break;
+       }
+       read_unlock(&vmlist_lock);
+
+       if (!area) {
+               xenbus_dev_error(dev, -ENOENT,
+                                "can't find mapped virtual address %p", vaddr);
+               return GNTST_bad_virt_addr;
+       }
+
+       op.handle = (grant_handle_t)area->phys_addr;
+
+       lock_vm_area(area);
+       BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
+       unlock_vm_area(area);
+
+       if (op.status == GNTST_okay)
+               free_vm_area(area);
+       else
+               xenbus_dev_error(dev, op.status,
+                                "unmapping page at handle %d error %d",
+                                (int16_t)area->phys_addr, op.status);
+
+       return op.status;
+}
+EXPORT_SYMBOL(xenbus_unmap_ring_vfree);
+
+
+int xenbus_unmap_ring(struct xenbus_device *dev,
+                    grant_handle_t handle, void *vaddr)
+{
+       struct gnttab_unmap_grant_ref op = {
+               .host_addr = (unsigned long)vaddr,
+               .handle    = handle,
+       };
+
+       BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
+
+       if (op.status != GNTST_okay)
+               xenbus_dev_error(dev, op.status,
+                                "unmapping page at handle %d error %d",
+                                handle, op.status);
+
+       return op.status;
+}
+EXPORT_SYMBOL(xenbus_unmap_ring);
+
+
 XenbusState xenbus_read_driver_state(const char *path)
 {
        XenbusState result;
index 21628b33514c8bc6c1e5416aa13325e1303d36d9..6cdd76652f9d41201e3a86b89ad8c979bf54f6e8 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/device.h>
 #include <linux/notifier.h>
 #include <asm/semaphore.h>
+#include <xen/interface/grant_table.h>
 #include <xen/interface/io/xenbus.h>
 #include <xen/interface/io/xs_wire.h>
 
@@ -208,6 +209,34 @@ int xenbus_switch_state(struct xenbus_device *dev,
 int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn);
 
 
+/**
+ * Map a page of memory into this domain from another domain's grant table.
+ * xenbus_map_ring_valloc allocates a page of virtual address space, maps the
+ * page to that address, and sets *vaddr to that address.
+ * xenbus_map_ring does not allocate the virtual address space (you must do
+ * this yourself!). It only maps in the page to the specified address.
+ * Returns 0 on success, and GNTST_* (see xen/include/public/grant_table.h) or
+ * -ENOMEM on error. If an error is returned, device will switch to
+ * XenbusStateClosing and the error message will be saved in XenStore.
+ */
+int xenbus_map_ring_valloc(struct xenbus_device *dev,
+                          int gnt_ref, void **vaddr);
+int xenbus_map_ring(struct xenbus_device *dev, int gnt_ref,
+                          grant_handle_t *handle, void *vaddr);
+
+
+/**
+ * Unmap a page of memory in this domain that was imported from another domain.
+ * Use xenbus_unmap_ring_vfree if you mapped in your memory with
+ * xenbus_map_ring_valloc (it will free the virtual address space).
+ * Returns 0 on success and returns GNTST_* on error
+ * (see xen/include/public/grant_table.h).
+ */
+int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr);
+int xenbus_unmap_ring(struct xenbus_device *dev,
+                     grant_handle_t handle, void *vaddr);
+
+
 /**
  * Allocate an event channel for the given xenbus_device, assigning the newly
  * created local port to *port.  Return 0 on success, or -errno on error.  On
@@ -217,6 +246,20 @@ int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn);
 int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port);
 
 
+/**
+ * Bind to an existing interdomain event channel in another domain. Returns 0
+ * on success and stores the local port in *port. On error, returns -errno,
+ * switches the device to XenbusStateClosing, and saves the error in XenStore.
+ */
+int xenbus_bind_evtchn(struct xenbus_device *dev, int remote_port, int *port);
+
+
+/**
+ * Free an existing event channel. Returns 0 on success or -errno on error.
+ */
+int xenbus_free_evtchn(struct xenbus_device *dev, int port);
+
+
 /**
  * Return the state of the driver rooted at the given store path, or
  * XenbusStateClosed if no state can be read.